﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sherwood.Content.DO.Model;
using Sherwood.Content.Tasks;
using Xtensive.Integrity.Transactions;
using Xtensive.Storage;
using Xtensive.Storage.Rse;

namespace Sherwood.Content.DO.Services
{
    public class TaskManager : ITaskManager
    {
        public TTask CreateNew<TTask>(params object[] parameters) where TTask : class, ITask
        {
            var type = typeof (TTask);
            if (!type.IsSubclassOf(typeof (Task)))
                throw new InvalidOperationException("TaskManager can only be used with sub classes of Task");

            foreach (var constructor in type.GetConstructors())
            {
                var pi = constructor.GetParameters();
                if(pi.Select(p => p.ParameterType).SequenceEqual(parameters.Select(p => p.GetType()), new IsAssignableFromComparer()))
                {
                    var task = (TTask) constructor.Invoke(parameters);
                    
                    var transaction = Session.Current.Transaction.Outermost;
                    List<Task> list;
                    if (!_watchingTransactions.TryGetValue(transaction, out list))
                    {
                        Session.Current.Events.TransactionCommitted += CurrentOnTransactionCommitted;
                        Session.Current.Events.TransactionRollbacked += CurrentOnTransactionCommitted;
                        _watchingTransactions.Add(transaction, list = new List<Task>());
                    }
                    list.Add(task as Task);

                    return task;
                }
            }
            throw new ArgumentException("Could not find a usable constructor for type " + type.FullName);
        }

        private readonly Dictionary<Transaction, List<Task>> _watchingTransactions = new Dictionary<Transaction, List<Task>>();

        private void CurrentOnTransactionCommitted(object sender, TransactionEventArgs transactionEventArgs)
        {
            var transaction = transactionEventArgs.Transaction;
            List<Task> list;
            if(!transaction.IsNested && _watchingTransactions.TryGetValue(transaction, out list))
            {
                if (transaction.State == TransactionState.Committed)
                    TasksCreated(this, new TasksCreatedEventArgs(list.Where(t => !t.IsRemoved())));
                _watchingTransactions.Remove(transaction);

                Session.Current.Events.TransactionCommitted -= CurrentOnTransactionCommitted;
                Session.Current.Events.TransactionRollbacked -= CurrentOnTransactionCommitted;
            }
        }

        public IEnumerable<ITask> GetExecutableTasks()
        {
            return Query.All<Task>()
                .Where(t => t.ExecutedDate == null && t.PlannedDate != null && t.PlannedDate <= DateTime.UtcNow)
                .Lock(LockMode.Update, LockBehavior.Skip);
        }

        public event EventHandler<TasksCreatedEventArgs> TasksCreated = delegate {};

        public DateTime GetNextTaskExecution()
        {
            var nextTaskExecution = Query.All<Task>()
                .Where(t => t.ExecutedDate == null && t.PlannedDate != null)
                .Lock(LockMode.Shared, LockBehavior.Skip)
                .Min(t => t.PlannedDate.GetValueOrDefault(DateTime.MaxValue));
            return nextTaskExecution == DateTime.MinValue ? DateTime.MaxValue : nextTaskExecution;
        }
    }
}
